/*
 * GeneticAlgorithm.cpp
 *
 *  Created on: Jan 16, 2012
 *      Author: luiz
 */

#include <algorithm>
#include <sstream>
#include <iostream>
#include <fstream>
#include <set>
#include <iomanip>
#include <map>

//#include <omp.h>

#include "GeneticAlgorithm.h"
#include "../Entity/RandomNumber.h"
#include "DecoderCircuitAdapter.h"
#include "ChromosomeDecoder.h"
#include "../Entity/Time.h"
#include "../Boundary/Config.h"
#include "../Model/MatrixPositionAndClockComparator.h"
#include "../Boundary/LatexOutput.h"
//#include "../Boundary/gnuplot_i.h"


GeneticAlgorithm::GeneticAlgorithm(Instance *instance, int population_size,int iterations):instance_(instance),population_size_(population_size),iterations_(iterations)
{

}

GeneticAlgorithm::~GeneticAlgorithm()
{
    // TODO Auto-generated destructor stub
}

void GeneticAlgorithm::evaluate_population()
{
    std::cout << "Individual #num #fitness #polarization #cells #pol_norm #cell_norm" << std::endl;
    Instance instance(*instance_);
    Layer layer(*layer_default_);
    //#pragma omp parallel for num_threads(2)
    for (int i = 0; i < population_reproduction_size_; ++i) {
        population_[i].calculate_fitness_with_layer_default(instance_, layer_default_,&population_[0]);
        std::cout << "R:";
        //std::cout << "Individual " << i << ": " << population_.at(i).fitness() << std::endl;
        std::cout <<  std::setprecision(5)
        << "Individual "
        << i << ":\t\t"
        << population_.at(i).fitness()
        << "\t\t"
        << population_.at(i).polarization_error()
        << "\t\t"
        << population_.at(i).layer()->get_number_of_cells()
        << "\t\t"
        << population_.at(i).polarization_error()/population_.at(0).polarization_error()
        << "\t\t"
        << population_.at(i).layer()->get_number_of_cells() /(double)population_.at(0).layer()->get_number_of_cells()
        << std::endl;

    }


    //#pragma omp parallel for num_threads(4)
    for (int i = population_reproduction_size_; i < (int)population_.size(); ++i) {
        population_[i].calculate_fitness_with_layer_default(instance_, layer_default_,&population_[0]);
        if((i-population_reproduction_size_)>=population_crossover_size_){
            std::cout << "M:";
        } else {
            std::cout << "C:";
        }
        std::cout <<  std::setprecision(5)
        << "Individual "
        << i << ":\t\t"
        << population_.at(i).fitness()
        << "\t\t"
        << population_.at(i).polarization_error()
        << "\t\t"
        << population_.at(i).layer()->get_number_of_cells()
        << "\t\t"
        << population_.at(i).polarization_error()/population_.at(0).polarization_error()
        << "\t\t"
        << population_.at(i).layer()->get_number_of_cells() /(double)population_.at(0).layer()->get_number_of_cells()
        << std::endl;

    }

    std::sort(population_.begin(),population_.end());
    std::cout << print_population_fitness() << std::endl;
}


void GeneticAlgorithm::save_statistics(std::vector<Samples> &results_statistics_generation,const std::map<int,Individual> &individuals) {

    std::string saida = simulation_->generate_statistics_name_sequence();
    std::ofstream myfile;
    std::cout <<" GRAVANDO: " << saida << std::endl;
    myfile.open (saida.c_str());
    myfile << "****************************" << std::endl;
    myfile << "RESULST\n";
    myfile << "INSTANCE:" << instance_->file_name() << std::endl;
    myfile << "DESCRIPTION:" << instance_->description() << std::endl;
    myfile << "INPUTS:"<< instance_->number_inputs() << "\n";


    //    // para cada porta de entrada
    //    for (int i = 0; i < instance_->num_outputs(); ++i) {
    //        // para cada valor da porta de entrada
    //        for (int j = 0; j < instance_->truth_table()->num_entry(); ++j) {
    //            myfile << "EXPECTED OUPUT "<< i << " :" << instance_->truth_table()->get_output_entry(i,j) << "\n";
    //        }
    //
    //    }
    //

    /*
     * Imprime Parametros da simulacao
     */

    myfile << "SIMULATION PARAMETER\n" <<
            "POPULATION SIZE:\t\t" << population_size_ << std::endl <<
            "CROSSOVER SIZE:\t\t" << population_crossover_size_ << "\t\t TAX:" << crossover_tax_ << std::endl <<
            "MUTATION SIZE:\t\t" << population_mutation_size_ << "\t\t TAX:" << mutation_tax_ <<std::endl <<
            "REPRODUCTION SIZE:\t\t" << population_reproduction_size_ << std::endl <<
            "EVALUATION FUNCTION:\t\t" << instance_->evaluation_function()->getName() << std::endl;

    myfile << "****************************" << std::endl;

    myfile << "STATISTICS\n LOW\tHIGH\tMED\tSD\n";

    for (int i = 0; i < (int)results_statistics_generation.size(); ++i) {
        myfile << results_statistics_generation[i].getLowerValue() << "\t" << results_statistics_generation[i].getHigherValue() << "\t" << results_statistics_generation[i].getAverage() << "\t" << results_statistics_generation[i].getSD() << "\n";
    }

    /*
     * Salva os 10% melhores individuos
     */
    for (int i = 0; i < population_.size()*0.1; ++i) {
        myfile << population_[i].get_chromossome().print_chromosome() << std::endl << std::endl;
    }


    /*
     * Salva estatisticas do melhor individuo
     */

    myfile << std::endl;
    for(std::map<int,Individual>::const_iterator it = individuals.begin(); it != individuals.end(); ++it) {
        myfile << "Individual: Cell\t" << it->second.layer()->number_of_cells() << " \t" << it->second.polarization_error()  << "\tValid:" << it->second.is_valid(*instance_) << std::endl;
    }


    myfile.close();

    /*
     * Salva os 10% melhores circuitos
     */
    ChromosomeDecoder decoder;
    for (int i = 0; i < population_.size()*0.1; ++i) {
        Layer layer = decoder.getChromossomeDecodedByLayerDefault(*instance_,population_[i].get_chromossome(),*layer_default_);
        DecoderCircuitAdapter adapter;
        std::string saida = simulation_->generate_circuit_name_sequence();
        adapter.print_layer_decoded(layer,saida);
    }



}


void GeneticAlgorithm::execute()
{
    //armazena os dados estatisticos da geracao
    std::vector<Samples> results_statistics_generation;
    // preenche primeira amostra

    results_statistics_generation.push_back(Samples());


    std::set<Chromosome> population_chromossome;


    std::map<int, Individual> best_individuals;


    /*
     * GERA A POPULACAO ALEATORIA
     */
    for (int i = 0; i < population_size_; ++i) {
        population_.push_back(Individual(instance_->number_of_cells()));
        population_.rbegin()->generate_random_chromosome();
        population_chromossome.insert(population_.rbegin()->get_chromossome());

    }


    // avalia toda a populacao inicial
    evaluate_population();

    for (int i = 0; i < (int)population_.size(); ++i) {
        results_statistics_generation.rbegin()->addSample(population_[i].fitness());
    }


    // inicia as iteracoes

    /*
     * GNUPLOT
//     */
    //
    //    gnuplot_ctrl    *   gnuplot_h1;
    //    gnuplot_h1 = gnuplot_init() ;

    //    double              gnuplot_x[iterations_] ;
    //    double              gnuplot_y[iterations_] ;

    /*
     * GNUPLOT
     */


    for (int i = 0; i < iterations_; ++i) {

        std::cout << "****************************** "
                "\n\n\t Iteration:" << i <<
                "\n\n******************************" << std::endl;

        std::vector<Individual> new_population;
        /*
         * REPRODUCAO
         */
        for (int x = 0;x < population_reproduction_size(); ++x) {
            new_population.push_back(population_[x]);
        }
        //        std::vector<Individual> reproduction_population = execute_reproduction();
        //      std::copy(reproduction_population.begin(),reproduction_population.end(),std::back_inserter(new_population));
        /*
         * MUTACAO
         */
        std::vector<Individual> new_population_mutation;

        while((int)new_population_mutation.size()<population_mutation_size()){
            //obtem os individuos filhos usando um metodo de selecao
            std::vector<int> selected_individuals = selection_reward_based_selection_population_index(this->population_mutation_size());
            //executa a mutacao
            std::vector<Individual> individuals = execute_mutation(selected_individuals);

            //adiciona os novos filhos somente se nao existirem na populacao
            for (int i = 0; (i < (int)individuals.size() && (int)new_population_mutation.size() < population_mutation_size()); ++i) {
                if(population_chromossome.find(individuals[i].get_chromossome())==population_chromossome.end()){
                    new_population_mutation.push_back(individuals[i]);
                }
            }
        }

        std::copy(new_population_mutation.begin(),new_population_mutation.end(),std::back_inserter(new_population));

        /*
         * CROSSOVER
         */
        std::vector<Individual> new_population_crossover;
        while((int)new_population_crossover.size()<(int)population_crossover_size()){
            //obtem os individuos filhos usando ranqueamento
            std::vector<Individual> individuals = execute_crossover(selection_reward_based_selection_population_index(this->population_crossover_size()));
            //adiciona os novos filhos somente se nao existirem na populacao
            for (int i = 0; i < (int)individuals.size() && (int)new_population_crossover.size() < (int)population_crossover_size(); ++i) {
                if(population_chromossome.find(individuals[i].get_chromossome())==population_chromossome.end()){
                    new_population_crossover.push_back(individuals[i]);
                }
            }

        }
        std::copy(new_population_crossover.begin(),new_population_crossover.end(),std::back_inserter(new_population));

        population_ = new_population;
        evaluate_population();

        for (int i = 0; i < population_size_; ++i) {
            std::map<int,Individual>::iterator map_it = best_individuals.find(population_[i].layer()->number_of_cells());
            if(map_it == best_individuals.end()){
                best_individuals[population_[i].layer()->number_of_cells()] = population_[i];
            } else {
                if(best_individuals[population_[i].layer()->number_of_cells()].polarization_error() > population_[i].polarization_error()){
                    best_individuals[population_[i].layer()->number_of_cells()] = population_[i];
                }
            }
        }

        //PRINT BEST INDIVIDUALS
        for(std::map<int,Individual>::iterator it = best_individuals.begin(); it != best_individuals.end(); ++it) {
            std::cout << "Individual: Cell\t" << it->second.layer()->number_of_cells() << " \t" << it->second.polarization_error()  << "\tValid:" << it->second.is_valid(*instance_) << std::endl;
        }

        // atualiza estatisticas
        results_statistics_generation.push_back(Samples());
        for (int x = 0; x < population_size_; ++x) {
            results_statistics_generation[results_statistics_generation.size()-1].addSample(population_[x].fitness());
        }

        ///salva o melhor da populacao
        //        ChromosomeDecoder decoder;
        //        Layer layer = decoder.getChromossomeDecodedByLayerDefault(*instance_,population_[0].get_chromossome(),*layer_default_);
        //        DecoderCircuitAdapter adapter;
        //        std::string saida = simulation_->generate_circuit_name_sequence();
        //        adapter.print_layer_decoded(layer,saida);
        save_individual(population_[0],"generation_best_individual");

        //        /*
        //         * GNU PLOT
        //         */
        //        if(i > 0){
        //            for (int x = 0; x < i+1; ++x) {
        //                gnuplot_x[x] = (double)x;
        //                gnuplot_y[x] = results_statistics_generation[x].getAverage();
        //            }
        //            gnuplot_resetplot(gnuplot_h1) ;
        //            gnuplot_setstyle(gnuplot_h1, "linespoints") ;
        //
        //            gnuplot_plot_xy(gnuplot_h1, gnuplot_x, gnuplot_y, i+1, "Fitness Médio") ;
        //            for (int x = 0; x < i+1; ++x) {
        //                gnuplot_y[x] = results_statistics_generation[x].getLowerValue();
        //
        //            }
        //            gnuplot_setstyle(gnuplot_h1, "linespoints") ;
        //            gnuplot_plot_xy(gnuplot_h1, gnuplot_x, gnuplot_y, i+1, "Melhor Fitness") ;
        //        }
        //        /*
        //         * GNUPLOT
        //         */



        //update chromossome population
        //        population_chromossome.clear();
        for (int i = 0; i < (int)population_.size(); ++i) {
            population_chromossome.insert(population_[i].get_chromossome());

        }

    }

    save_individuals(best_individuals,"best_individuals");
    save_statistics(results_statistics_generation,best_individuals);
    //int a;
    //std::cin >> a;
    //    gnuplot_close(gnuplot_h1) ;
    // std::cout << "\nBEST INDIVIDUALS" << std::endl;


}

std::string GeneticAlgorithm::print_population_fitness()
{
    std::stringstream out_text;
    out_text << "\n" ;
    for (int i = 0; i < (int)population_.size(); ++i) {
        out_text<<"Individual " << i << ": " << population_.at(i).fitness() << "\n";

    }
    return out_text.str();
}

std::vector<int> GeneticAlgorithm::selection_roulette_selector_population_index(int number_of_individual_selected,int window)
{
    std::vector<int> selected_individuals;
    for (int i = 0; i < number_of_individual_selected; ++i) {

        int selected_individual = RandomNumber::get_instance().getRandomInteger(population_.size());
        double fitness_selected_individual = population_[selected_individual].fitness();
        for (int j = 1; j < window; ++j) {
            int individual_index =  RandomNumber::get_instance().getRandomInteger(population_.size());
            if(fitness_selected_individual > population_[individual_index].fitness()){
                selected_individual = individual_index;
                fitness_selected_individual = population_[individual_index].fitness();
            }
        }
        selected_individuals.push_back(selected_individual);
    }
    return selected_individuals;
}

std::vector<Individual> GeneticAlgorithm::execute_mutation(std::vector<int> index_individual_list)
{
    std::vector<Individual> individuals;
    for (int i = 0; i < (int)index_individual_list.size(); ++i) {
        individuals.push_back(population_[index_individual_list[i]].mutation(mutation_tax_));
        //  individuals[i].calculate_fitness_with_layer_default(*instance_,layer_default_);
    }

    return individuals;
}

void GeneticAlgorithm::set_mutation_tax(double tax)
{
    mutation_tax_ = tax;
}

double GeneticAlgorithm::mutation_tax()
{
    return mutation_tax_;
}




void GeneticAlgorithm::set_layer_default(Layer *layer)
{
    layer_default_ = layer;
}



std::vector<Individual> GeneticAlgorithm::execute_crossover(std::vector<int> index_individual_list)
{
    std::vector<Individual> individuals;
    for (int i = 0; i < (int)index_individual_list.size()-1; i+=2) {
        //  std::cout << "\n\nIMPRIMINDO:" << std::endl;
        // std::cout << "PAI 1:" << population_[index_individual_list[i]].print()  << std::endl;
        //std::cout << "PAI 2:" << population_[index_individual_list[i+1]].print()  << std::endl << std::endl;
        //std::fflush(stdout);
        std::vector<Individual> offsprings = population_[index_individual_list[i]].crossover(population_[index_individual_list[i+1]]);

        //std::cout << "FILHO 1:" << offsprings[0].print()  << std::endl;
        // std::cout << "FILHO 2:" << offsprings[1].print()  << std::endl; //<< std::endl
        //std::cout << "SIZE:" << offsprings.size() << std::endl;
        for (int j = 0; j < (int)offsprings.size(); ++j) {
            individuals.push_back(offsprings[j]);
            //individuals[i].calculate_fitness_with_layer_default(*instance_, layer_default_);
        }
    }

    return individuals;
}

void GeneticAlgorithm::set_crossover_tax(double tax)
{
    this->crossover_tax_ = tax;
}

double GeneticAlgorithm::crossover_tax()
{
    return this->crossover_tax_;
}

std::vector<int> GeneticAlgorithm::selection_reward_based_selection_population_index(int number_of_individual_selected)
{
    std::vector<int> selected_individuals;
    std::vector<double> rewards;
    double acc_reward = 0;
    int rewards_sum = ((int)population_.size()*(population_.size()+1))/2;
    for (int i = 0; i < (int)population_.size(); ++i) {
        rewards.push_back(((population_.size()-i)/(double)rewards_sum)+acc_reward);
        acc_reward  = rewards[i];
    }
    for (int i = 0; i < number_of_individual_selected; ++i) {
        double selected_reward = RandomNumber::get_instance().getRandonDouble();
        for (int j = 0; j < (int)rewards.size(); ++j) {
            if(selected_reward < rewards[j] ) {
                selected_individuals.push_back(j);
                break;
            }
        }
    }
    //    std::cout << "DEBUG: ";
    //    for (int i = 0; i < (int)selected_individuals.size(); ++i) {
    //        std::cout << selected_individuals[i] << ",";
    //    }
    //  std::cout << std::endl;
    return selected_individuals;
}

void GeneticAlgorithm::set_population_crossover_size(int size)
{
    this->population_crossover_size_ = size;
}

void GeneticAlgorithm::set_population_mutation_size(int size)
{
    this->population_mutation_size_= size;
}

int GeneticAlgorithm::population_mutation_size()
{
    return population_mutation_size_;
}

int GeneticAlgorithm::population_crossover_size()
{
    return population_crossover_size_;
}

void GeneticAlgorithm::set_population_reproduction_size(int size)
{
    this->population_reproduction_size_ = size;
}

void GeneticAlgorithm::set_population_random_size(int size)
{
    this->population_random_size_ = size;
}

int GeneticAlgorithm::population_reproduction_size()
{
    return population_reproduction_size_;
}

int GeneticAlgorithm::population_random_size()
{
    return population_random_size_;
}

Layer *GeneticAlgorithm::layer_default()
{
    return layer_default_;
}

Simulation *GeneticAlgorithm::simulation()
{
    return simulation_;
}

void GeneticAlgorithm::set_simulation(Simulation *simulation)
{
    this->simulation_ = simulation;
}

void GeneticAlgorithm::save_individuals(const std::vector<Individual> & individuals,const std::string base_name)
{
    DecoderCircuitAdapter adapter;
    std::vector<Individual>::const_iterator individual_iterator = individuals.begin();
    while(individual_iterator!=individuals.end())
    {
        Individual individual = Individual(*(individual_iterator));
        adapter.print_layer_decoded(*individual.layer(),simulation_->generate_circuit_name_sequence(base_name));
        ++individual_iterator;
    }
}

void GeneticAlgorithm::save_individuals(const std::map<int,Individual> & individuals,const std::string base_name)
{

    std::map<int,Individual>::const_iterator individual_iterator = individuals.begin();
    while(individual_iterator!=individuals.end())
    {
        Individual individual = Individual(individual_iterator->second);
        std::stringstream oss;
        oss << base_name << "_" << individual.layer()->number_of_cells();
        save_individual(individual,oss.str());
        ++individual_iterator;
    }
}

void GeneticAlgorithm::save_individual(const Individual & individual_p,const std::string &file_name)
{
    DecoderCircuitAdapter adapter;
    Individual individual = individual_p;
    adapter.print_layer_decoded(*individual.layer(),simulation_->generate_circuit_name_sequence(file_name));
    LatexOutput latex;
    latex.save_instance_latex(simulation_->generate_tex_name_sequence(file_name),*individual.layer());
}

void GeneticAlgorithm::clear_execution()
{
    population_.clear();

}

std::vector<Individual> GeneticAlgorithm::execute_reproduction()
{
    std::vector<Individual> reproduction_individuals;
    reproduction_individuals.push_back(population_[0]);
    MatrixPositionAndClockComparator comparator;
    std::vector<Individual>::iterator population_iterator = population_.begin();

    //    ChromosomeDecoder

    while((int)reproduction_individuals.size() != population_reproduction_size_ || population_iterator == population_.end()){
        int flag_insertion = true;
        for (int j = 0; j < (int)reproduction_individuals.size(); ++j) {
            if(reproduction_individuals[j].layer() == NULL){
                //                reproduction_individuals[j].set_layer()
            }
            double distance = comparator.compare(*reproduction_individuals[j].layer(),*population_iterator->layer());
            if( distance < 0.3  ){
                flag_insertion = false;
            }
        }
        if(flag_insertion){
            reproduction_individuals.push_back(*population_iterator);
        }
        ++population_iterator;
    }
    if( ((int)reproduction_individuals.size()) < population_reproduction_size_){
        population_iterator = population_.begin();
        while((int)reproduction_individuals.size() != population_reproduction_size_){
            int flag_insertion = true;
            for (int j = 0; j < (int)reproduction_individuals.size(); ++j) {
                double distance = comparator.compare(*reproduction_individuals[j].layer(),*population_iterator->layer());
                if( distance  == 0 ){
                    flag_insertion = false;
                }
            }
            if(flag_insertion){
                reproduction_individuals.push_back(*population_iterator);
            }
            ++population_iterator;
        }
    }
    return reproduction_individuals;
}














